package com.wdb.aspect;

import com.wdb.domain.SysLog;
import com.wdb.service.LogService;
import com.wdb.utils.RequestHolder;
import com.wdb.utils.SecurityUtils;
import com.wdb.utils.StringUtils;
import com.wdb.utils.ThrowableUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.weaver.ast.Var;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @author: wendaobai@qq.com
 * @date: 2024/9/10 星期二 9:46
 */
@Aspect
@Slf4j
@Component
public class LogAspect {
  private final LogService logService;
  ThreadLocal<Long> currentTime = new ThreadLocal<>();

  public LogAspect(LogService service) {
    this.logService = service;
  }

  @Pointcut("@annotation(com.wdb.annotation.Log)")
  public void logPointcut() {
    // log.info("--xxxx");
  }

  /**
   * 环绕通知，用于在目标方法执行前后记录日志 该方法通过@Around注解指定的切点(logPointcut())来包围目标方法的执行
   *
   * @param joinPoint 连接点对象，提供了关于当前执行的目标方法的信息
   * @return 目标方法的执行结果
   * @throws Throwable 如果目标方法执行过程中抛出异常，则转发该异常
   */
  @Around("logPointcut()")
  public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    Object result;
    // 在目标方法执行前记录当前时间
    currentTime.set(System.currentTimeMillis());
    // 继续执行目标方法，并获取其结果
    result = joinPoint.proceed();
    // 计算目标方法执行时间，并创建SysLog对象记录日志信息
    SysLog sysLog = new SysLog("INFO", System.currentTimeMillis() - currentTime.get());
    currentTime.remove();
    // 获取当前HTTP请求对象
    HttpServletRequest request = RequestHolder.gethttpServletRequest();
    // 保存系统日志，包括用户信息、浏览器信息、IP地址、方法信息和日志信息
    logService.save(
        getUsername(),
        StringUtils.getBrowser(request),
        StringUtils.getIp(request),
        joinPoint,
        sysLog);
    // log.info("---aa");
    // 返回目标方法的执行结果
    return result;
  }

  // logPointcut异常通知
  @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
  public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
    // 创建系统日志对象，记录异常类型为ERROR，并计算执行时间
    SysLog sysLog = new SysLog("ERROR", System.currentTimeMillis() - currentTime.get());
    // 移除当前时间记录，因为异常发生后不再需要记录时间差
    currentTime.remove();
    // 设置异常详细信息，记录异常堆栈信息
    sysLog.setExceptionDetail(new String(ThrowableUtil.getStackTrace(e).getBytes()));
    // 保存日志信息，包括用户名、浏览器信息、IP地址、方法签名和日志详情
    logService.save(
        getUsername(),
        StringUtils.getBrowser(RequestHolder.gethttpServletRequest()),
        StringUtils.getIp(RequestHolder.gethttpServletRequest()),
        (ProceedingJoinPoint) joinPoint,
        sysLog);
  }

  private String getUsername() {
    String currentUsername = SecurityUtils.getCurrentUsername();
    return currentUsername;
  }
}
